﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine.Timeline;
using UnityEngine;
using System.Collections;

public class FM_AudioTrack
{
    private const string TAG = Constants.TAG;
    public int AccessIndex = 0; // Used to remove old, unused tracks
    private readonly FM_AudioSubTrack track1, track2, track3, track4, track5, track6, track7;
    private bool loading;
    //private readonly Context context;
    private short[] output;

    public bool Check(FM_AudioSubTrack track1, FM_AudioSubTrack track2, FM_AudioSubTrack track3, FM_AudioSubTrack track4, FM_AudioSubTrack track5, FM_AudioSubTrack track6, FM_AudioSubTrack track7)
    {
        List<FM_AudioSubTrack> self = new List<FM_AudioSubTrack>();
        List<FM_AudioSubTrack> checkedTracks = new List<FM_AudioSubTrack>();
        if (this.track1 != null) self.Add(this.track1);
        if (this.track2 != null) self.Add(this.track2);
        if (this.track3 != null) self.Add(this.track3);
        if (this.track4 != null) self.Add(this.track4);
        if (this.track5 != null) self.Add(this.track5);
        if (this.track6 != null) self.Add(this.track6);
        if (this.track7 != null) self.Add(this.track7);
        self.Sort();
        if (track1 != null) checkedTracks.Add(track1);
        if (track2 != null) checkedTracks.Add(track2);
        if (track3 != null) checkedTracks.Add(track3);
        if (track4 != null) checkedTracks.Add(track4);
        if (track5 != null) checkedTracks.Add(track5);
        if (track6 != null) checkedTracks.Add(track6);
        if (track7 != null) checkedTracks.Add(track7);
        checkedTracks.Sort();
        if (self.Count != checkedTracks.Count) return false;
        return self.SequenceEqual(checkedTracks);
    }

    public FM_AudioTrack(FM_AudioSubTrack track1, FM_AudioSubTrack track2, FM_AudioSubTrack track3, FM_AudioSubTrack track4, FM_AudioSubTrack track5, FM_AudioSubTrack track6, FM_AudioSubTrack track7)
    {
        this.track1 = track1;
        this.track2 = track2;
        this.track3 = track3;
        this.track4 = track4;
        this.track5 = track5;
        this.track6 = track6;
        this.track7 = track7;

        return;
        this.loading = true;

        try
        {
            short[] music1 = null;
            short[] music2 = null;
            short[] music3 = null;
            short[] music4 = null;
            short[] music5 = null;
            short[] music6 = null;
            short[] music7 = null;
            int td1 = 0;
            int td2 = 0;
            int td3 = 0;
            int td4 = 0;
            int td5 = 0;
            int td6 = 0;
            int td7 = 0;
            int l = 0;
            int fallback = FM_SoundPool.FALLBACK_DURATION * 24;

            float[] values = new float[0];
            if (track1 != null)
            {
                string assetFile = FM_SoundPool.assetFiles[track1.track];// System.IO.Path.GetFileNameWithoutExtension(assetFile);
                assetFile = assetFile.Substring(0,assetFile.LastIndexOf("."));
                var ac = Resources.Load<AudioClip>(assetFile);
                if (ac == null)
                    Debug.LogError($"path:{FM_SoundPool.assetFiles[track1.track]} ac == null:{assetFile}");
                ac.GetData(values, 0);
                music1 = values.Map(e => (short)e).AsArray();
                 td1 = track1.duration * 24 + fallback;
                l = td1;
            }
            if (track2 != null)
            {
                string assetFile = FM_SoundPool.assetFiles[track2.track];
                assetFile = assetFile.Substring(0, assetFile.LastIndexOf("."));
                var ac = Resources.Load<AudioClip>(assetFile);
                if (ac == null)
                    Debug.LogError($"ac == null:{assetFile}");
                ac.GetData(values, 0);
                music2 = values.Map(e => (short)e).AsArray();
                td2 = track2.duration * 24 + fallback;
                if (l < td2) l = td2;
            }
            if (track3 != null)
            {
                string assetFile = FM_SoundPool.assetFiles[track3.track];
                assetFile = assetFile.Substring(0, assetFile.LastIndexOf("."));
                var ac = Resources.Load<AudioClip>(assetFile);
                if (ac == null)
                    Debug.LogError($"ac == null:{assetFile}");
                ac.GetData(values, 0);
                music3 = values.Map(e => (short)e).AsArray();
                td3 = track3.duration * 24 + fallback;
                if (l < td3) l = td3;
            }
            if (track4 != null)
            {
                string assetFile = FM_SoundPool.assetFiles[track4.track];
                assetFile = assetFile.Substring(0, assetFile.LastIndexOf("."));
                var ac = Resources.Load<AudioClip>(assetFile);
                if (ac == null)
                    Debug.LogError($"ac == null:{assetFile}");
                ac.GetData(values, 0);
                music4 = values.Map(e => (short)e).AsArray();
                td4 = track4.duration * 24 + fallback;
                if (l < td4) l = td4;
            }
            if (track5 != null)
            {
                string assetFile = FM_SoundPool.assetFiles[track5.track];
                assetFile = assetFile.Substring(0, assetFile.LastIndexOf("."));
                var ac = Resources.Load<AudioClip>(assetFile);
                if (ac == null)
                    Debug.LogError($"ac == null:{assetFile}");
                ac.GetData(values, 0);
                music5 = values.Map(e => (short)e).AsArray();
                td5 = track5.duration * 24 + fallback;
                if (l < td5) l = td5;
            }
            if (track6 != null)
            {
                string assetFile = FM_SoundPool.assetFiles[track6.track];
                assetFile = assetFile.Substring(0, assetFile.LastIndexOf("."));
                var ac = Resources.Load<AudioClip>(assetFile);
                if (ac == null)
                    Debug.LogError($"ac == null:{assetFile}");
                ac.GetData(values, 0);
                music6 = values.Map(e => (short)e).AsArray();
                td6 = track6.duration * 24 + fallback;
                if (l < td6) l = td6;
            }
            if (track7 != null)
            {
                string assetFile = FM_SoundPool.assetFiles[track7.track];
                assetFile = assetFile.Substring(0, assetFile.LastIndexOf("."));
                var ac = Resources.Load<AudioClip>(assetFile);
                if (ac == null)
                    Debug.LogError($"ac == null:{assetFile}");
                ac.GetData(values, 0);
                music7 = values.Map(e => (short)e).AsArray();
                td7 = track7.duration * 24 + fallback;
                if (l < td7) l = td7;
            }

            output = new short[l];
            double[] outputTmp = new double[l];
            double max = 0;

            for (int i = 0; i < output.Length; i++)
            {
                double sample1 = 0;
                double sample2 = 0;
                double sample3 = 0;
                double sample4 = 0;
                double sample5 = 0;
                double sample6 = 0;
                double sample7 = 0;
                if (track1 != null && i < td1)
                {
                    double adjust = 1;
                    if (td1 - i < fallback) adjust = (td1 - i) / (fallback * 1.0);
                    sample1 = adjust * music1[i];
                }
                if (track2 != null && i < td2)
                {
                    double adjust = 1;
                    if (td2 - i < fallback) adjust = (td2 - i) / (fallback * 1.0);
                    sample2 = adjust * music2[i];
                }
                if (track3 != null && i < td3)
                {
                    double adjust = 1;
                    if (td3 - i < fallback) adjust = (td3 - i) / (fallback * 1.0);
                    sample3 = adjust * music3[i];
                }
                if (track4 != null && i < td4)
                {
                    double adjust = 1;
                    if (td4 - i < fallback) adjust = (td4 - i) / (fallback * 1.0);
                    sample4 = adjust * music4[i];
                }
                if (track5 != null && i < td5)
                {
                    double adjust = 1;
                    if (td5 - i < fallback) adjust = (td5 - i) / (fallback * 1.0);
                    sample5 = adjust * music5[i];
                }
                if (track6 != null && i < td6)
                {
                    double adjust = 1;
                    if (td6 - i < fallback) adjust = (td6 - i) / (fallback * 1.0);
                    sample6 = adjust * music6[i];
                }
                if (track7 != null && i < td7)
                {
                    double adjust = 1;
                    if (td7 - i < fallback) adjust = (td7 - i) / (fallback * 1.0);
                    sample7 = adjust * music7[i];
                }
                double mixed = sample1 + sample2 + sample3 + sample4 + sample5 + sample6 + sample7;
                if (mixed > max) max = mixed;
                outputTmp[i] = mixed;
            }
            double adjust2 = 1;
            if (max > 32768) adjust2 = 32768.0 / max;
            for (int i = 0; i < output.Length; i++) output[i] = (short)(outputTmp[i] * adjust2);

            loading = false;
        }
        catch (Exception ignored)
        {
            Debug.LogError(ignored.Message + "\n" +ignored.StackTrace);
        }
    }

    private static short SwapBytes(byte byte0, byte byte1)
    {
        return (short)((byte1 & 0xff) << 8 | (byte0 & 0xff));
    }

    private byte[] SampleToByteArray(Stream sample)
    {
        MemoryStream bOutStream = new MemoryStream();
        BufferedStream bis = new BufferedStream(sample);
        byte[] header = new byte[44];
        if (bis.Read(header, 0, header.Length) == -1)
            throw new IOException();

        int bufferSize = 1024 * 8;
        byte[] buffer = new byte[bufferSize];

        while (bis.Read(buffer, 0, buffer.Length) != -1)
        {
            bOutStream.Write(buffer, 0, buffer.Length);
        }

        byte[] outputByteArray = bOutStream.ToArray();
        bis.Close();
        bOutStream.Close();

        return outputByteArray;
    }

    private short[] SampleToShortArray(Stream sample)
    {
        short[] outputArray = new short[sample.Length / 2];
        byte[] outputByteArray = SampleToByteArray(sample);

        for (int i = 0, j = 0; i < outputByteArray.Length; i += 2, j++)
        {
            try
            {
                outputArray[j] = SwapBytes(outputByteArray[i], outputByteArray[i + 1]);
            }
            catch (Exception ignored)
            {
                Debug.LogError(ignored.Message);
            }
        }

        return outputArray;
    }

    public void Play(MonoBehaviour mono,long duration, bool nextPause, bool mute)
    {
        mono.StartCoroutine(IEPlayTrack(duration,  nextPause,  mute));
        //if (playing) return;
        //if (output.Length == 0) return;

        //new System.Threading.Thread(() =>
        //{
        //    while (loading) System.Threading.Thread.Sleep(10);

        //    //AudioController.instance.PlaySound();

        //    //AudioTrack audioTrack = new AudioTrack()
        //    //    .SetTransferMode(AudioTrackMode.Static)
        //    //    .SetPerformanceMode(AudioTrackPerformanceMode.LowLatency)
        //    //    .SetAudioAttributes(new AudioAttributes.Builder()
        //    //        .SetUsage(AudioUsageKind.Media)
        //    //        .SetContentType(AudioContentType.Music)
        //    //        .Build())
        //    //    .SetAudioFormat(new AudioFormat.Builder()
        //    //        .SetEncoding(AudioEncoding.Pcm16bit)
        //    //        .SetSampleRate(24000)
        //    //        .SetChannelMask(ChannelOut.Mono)
        //    //        .Build())
        //    //    .SetBufferSizeInBytes(output.Length * 2)
        //    //    .Build();

        //    //if (!mute)
        //    //{
        //    //    audioTrack.PlaybackHeadPosition = 44;
        //    //    audioTrack.Write(output, 0, output.Length);
        //    //    audioTrack.Play();
        //    //}

        //    FM_SoundPool.CustomDelay(duration, false);
        //    float fall = FM_SoundPool.FALLBACK_DURATION;

        //    if (nextPause)
        //        fall = fall / 5f;

        //    FM_SoundPool.CustomDelay((long)fall, true);

        //    //if (!mute)
        //    //{
        //    //    audioTrack.Stop();
        //    //}

        //    //audioTrack.Release();
        //}).Start();
    }

    public IEnumerator IEPlayTrack(long duration, bool nextPause, bool mute)
    {
        //Debug.LogError($"Run key:{key} clip:{clip!= null}");
        AudioClip clip1 = null, clip2 = null, clip3 = null, clip4 = null, clip5 = null, clip6 = null, clip7 = null;
        if (track1 != null)
            FM_SoundPool.soundMap.TryGetValue(this.track1.track, out clip1);
        if (track2 != null)
            FM_SoundPool.soundMap.TryGetValue(this.track2.track, out clip2);
        if(track3 != null)
            FM_SoundPool.soundMap.TryGetValue(this.track3.track, out clip3);
        if (track4 != null)
            FM_SoundPool.soundMap.TryGetValue(this.track4.track, out clip4);
        if (track5 != null)
            FM_SoundPool.soundMap.TryGetValue(this.track5.track, out clip5);
        if (track6 != null)
            FM_SoundPool.soundMap.TryGetValue(this.track6.track, out clip6);
        if (track7 != null)
            FM_SoundPool.soundMap.TryGetValue(this.track7.track, out clip7);

        List<AudioSource> asources = new List<AudioSource>();
        if (clip1 != null)
            asources.Add(AudioController.instance.Play(clip1));
        if (clip2 != null)
            asources.Add(AudioController.instance.Play(clip2));
        if (clip3 != null)
            asources.Add(AudioController.instance.Play(clip3));
        if (clip4 != null)
            asources.Add(AudioController.instance.Play(clip4));
        if (clip5 != null)
            asources.Add(AudioController.instance.Play(clip5));
        if (clip6 != null)
            asources.Add(AudioController.instance.Play(clip6));
        if (clip7 != null)
            asources.Add(AudioController.instance.Play(clip7));

        yield return new WaitForSeconds(duration / 1000f);
        //float timer = 0;
        //while(timer < waitTime)
        //{
        //    timer += Time.deltaTime;
        //    yield return 0;
        //}

        if (!mute)
        {
            float d = 0f;
            float fall = .2f;
            while (d < fall)
            {
                d += Time.deltaTime;
                float p = 1f - d / fall;
                foreach (var asource in asources)
                    asource.volume = p;

                //Debug.LogError($"d:{d}  fall:{fall}");
                yield return null;
            }
            foreach (var asource in asources)
                asource.volume = 0;
        }
    }
}
